/* verilator lint_off UNUSEDSIGNAL */
/* verilator lint_off UNOPTFLAT */
`include "defines.svh"
`default_nettype wire


module ID_STAGE(
    input clk,
    input reset,

    input logic if_valid,
    output logic id_ready,
    output logic id_valid,
    input logic exe_ready,

    input  if_to_id_bus_t if_to_id_bus,
    output id_to_exe_bus_t id_to_exe_bus,
    input  wb_to_id_bus_t wb_to_id_bus,
    
    // forward
    input  mem_to_id_bypass_t mem_to_id_bypass,
    output id_to_if_bypass_t id_to_if_bypass,
    input  exe_to_id_bypass_t exe_to_id_bypass,
    input  exe_to_front_bypass_t exe_to_front_bypass,
    input wb_to_front_bypass_t wb_to_front_bypass,

    // sram
    input word_t inst_sram_rdata,
    input logic  axi_inst_flushreq
);


word_t inst_sram_rdata_r;
if_to_id_bus_t if_to_id_bus_r;

//TODO:Once exe stage meets LOAD，stall id stage 1clk.need to be optimized.
logic brjmp_en;
logic fd_shake,de_shake,stall_id,flush_id;
assign fd_shake = if_valid & id_ready;
assign de_shake = exe_ready & id_valid;
assign stall_id = exe_to_front_bypass.load_hazard_en | exe_to_front_bypass.store_hazard_en;
assign flush_id = wb_to_front_bypass.ecall_en | brjmp_en | axi_inst_flushreq;
always_ff @(posedge clk) begin
    if(reset) begin
        id_valid <= `OFF;
    end else begin
        id_valid <= fd_shake;
    end
end
assign id_ready = exe_ready;

// 接收
always_ff @(posedge clk) begin
    if(reset) begin
        if_to_id_bus_r <= `NULL;
        inst_sram_rdata_r <= `NULL;
    end else if (fd_shake & ~stall_id & ~flush_id) begin
        if_to_id_bus_r <= if_to_id_bus;
        inst_sram_rdata_r <= inst_sram_rdata;
    end else if(flush_id & ~stall_id) begin
        if_to_id_bus_r <= `NULL;
        inst_sram_rdata_r <= `NULL;
    end
end


rfaddr_t rs1,rs2,rd;
rfcaddr_t csr;
word_t id_pc,id_inst;
word_t src1,src2,src_csr;
logic sel_rf_waddr;
logic[1:0] sel_src1;
logic[2:0] sel_src2,sel_imm;
logic[3:0] sel_brjmp;
word_t imm;
word_t rdata1,rdata2;
word_t rf_rdata1,rf_rdata2;

logic exe_forward1,mem_forward1,exe_forward2,mem_forward2;

word_t brjmp_pc,mret_pc;

// 发送
always_comb begin
    if(reset) begin
        id_to_exe_bus = `NULL;
    end else begin
        id_to_exe_bus.alu_src1 = src1;
        id_to_exe_bus.alu_src2 = src2;
        id_to_exe_bus.pc = id_pc;
        id_to_exe_bus.debug_dnpc = if_to_id_bus_r.debug_dnpc;
        id_to_exe_bus.inst = id_inst;
        id_to_exe_bus.store_data = rdata2;
        id_to_exe_bus.rf_waddr = sel_rf_waddr==`RFaddr_RD ? rd : 5'b0;
        id_to_exe_bus.rfc_waddr = csr;
    end
end



always_comb begin
    if(reset == `ON) begin 
        id_pc = `NULL;
        id_inst = `NULL;
        {rs1,rs2,rd,csr} = `NULL;
        {src1,src2} = `NULL;
        // sel_rf_waddr = `NULL;
        // {sel_src1,sel_src2} = `NULL;
        // sel_imm = `NULL;
        // sel_brjmp = `NULL;
        // imm = `NULL;
        {rdata1,rdata2} = `NULL;
        // {rf_rdata1,rf_rdata2} = `NULL;

        {exe_forward1,exe_forward2,mem_forward1,mem_forward2} = `NULL;

        {brjmp_pc,brjmp_en} = `NULL;
    end else begin
        id_pc = if_to_id_bus_r.pc;
        id_inst = inst_sram_rdata_r;
        rs1 = id_inst[19:15];
        rs2 = id_inst[24:20];
        rd  = id_inst[11:7];
        csr = id_inst[31:20];
        
        case(sel_src1)
            `S1_PC : src1 = id_pc;
            `S1_RF1: src1 = rdata1;
            default: src1 = `NULL;
        endcase

        case(sel_src2)
            `S2_RF2: src2 = rdata2;
            `S2_IMM: src2 = imm;
            `S2_4  : src2 = 32'h4;
            `S2_SA : src2 = {27'b0,imm[4:0]}; //之后可以去掉
            `S2_CSR: src2 = src_csr;
            default: src2 = `NULL;
        endcase

        case(sel_brjmp)
            `BEQ : begin
                brjmp_en = rdata1 == rdata2;
                brjmp_pc = id_pc + imm;
            end
            `BNE : begin
                brjmp_en = rdata1 != rdata2;
                brjmp_pc = id_pc + imm;
            end
            `BGE : begin
                brjmp_en = $signed(rdata1) >= $signed(rdata2);
                brjmp_pc = id_pc + imm;
            end
            `BGEU : begin
                brjmp_en = rdata1 >= rdata2;
                brjmp_pc = id_pc + imm;
            end
            `BLT : begin
                brjmp_en = $signed(rdata1) < $signed(rdata2);
                brjmp_pc = id_pc + imm;
            end
            `BLTU : begin
                brjmp_en = rdata1 < rdata2;
                brjmp_pc = id_pc + imm;
            end
            `JAL : begin
                brjmp_en = `ON;
                brjmp_pc = id_pc + imm;
            end
            `JALR: begin
                brjmp_en = `ON;
                brjmp_pc = (imm + rdata1)&~32'b1;
            end
            `MRET: begin
                brjmp_en = `ON;
                brjmp_pc = mret_pc;
            end
            default: begin
                brjmp_en = `OFF;
                brjmp_pc = `NULL;
            end
        endcase

        exe_forward1 = exe_to_id_bypass.rf_wen && exe_to_id_bypass.rf_waddr == rs1 && rs1 != 5'b0;
        mem_forward1 = mem_to_id_bypass.rf_wen && mem_to_id_bypass.rf_waddr == rs1 && rs1 != 5'b0;
        exe_forward2 = exe_to_id_bypass.rf_wen && exe_to_id_bypass.rf_waddr == rs2 && rs2 != 5'b0;
        mem_forward2 = mem_to_id_bypass.rf_wen && mem_to_id_bypass.rf_waddr == rs2 && rs2 != 5'b0;

        rdata1 = exe_forward1 ? exe_to_id_bypass.rf_wdata : 
                 mem_forward1 ? mem_to_id_bypass.rf_wdata : rf_rdata1;
        rdata2 = exe_forward2 ? exe_to_id_bypass.rf_wdata : 
                 mem_forward2 ? mem_to_id_bypass.rf_wdata : rf_rdata2;
    end
end

Decoder dec1(
    .clock(clk),
    .reset(reset),
    .io_inst(id_inst),
    .io_out_sel_brjmp(sel_brjmp),
    .io_out_sel_Alu_Src1(sel_src1),
    .io_out_sel_Alu_Src2(sel_src2),
    .io_out_sel_Alu_op(id_to_exe_bus.sel_alu_op),
    .io_out_sel_Mask(id_to_exe_bus.sel_mask),
    .io_out_DataSram_en(id_to_exe_bus.dse),
    .io_out_DataSram_wen(id_to_exe_bus.dsw),
    .io_out_RegFile_wen(id_to_exe_bus.rfw),
    .io_out_sel_RF_Waddr(sel_rf_waddr),
    .io_out_sel_RF_Wdata(id_to_exe_bus.sel_rf_wdata),
    .io_out_sel_Exception(id_to_exe_bus.sel_exception),
    .io_out_sel_Imm(sel_imm)
);


identIMM id_identIMM(
    .instr(id_inst),
    .imm_sel(sel_imm),
    .imm(imm)
);


regfile rf1(
    .clk    (clk),
    .rst    (reset),
    .en1    (`ON),
    .raddr1 (rs1),
    .rdata1 (rf_rdata1),
    .en2    (`ON),
    .raddr2 (rs2),
    .rdata2 (rf_rdata2),
    .wen    (wb_to_id_bus.rf_wen   ),
    .waddr  (wb_to_id_bus.rf_waddr ),
    .wdata  (wb_to_id_bus.rf_wdata )
);

//TODO:CSR的forward旁路
word_t ecall_pc;// id级遇到ecall时就把异常入口的pc记下来，等exe/wb需要异常跳转时，就直接用保存的。
// logic except_en;
// word_t except_pc,except_inst;
regfile_csr rf_csr1(
    .clk        (clk),
    .rst        (reset),

    .wen        (wb_to_id_bus.rfc_wen   ),
    .waddr      (wb_to_id_bus.rfc_waddr ),
    .wdata      (wb_to_id_bus.rfc_wdata ),

    .en         (`ON),
    .raddr      (csr),
    .rdata      (src_csr),

    .except_en  (wb_to_front_bypass.ecall_en),
    .pc_i       (wb_to_front_bypass.wb_ecall_pc),
    .inst_i     (wb_to_front_bypass.wb_ecall_inst),
    .cause_i    ('hb),
    .mtvec_o    (ecall_pc),
    .mepc_o     (mret_pc)
);

assign id_to_if_bypass.brjmp_en = (brjmp_en & id_valid) | exe_to_front_bypass.ecall_en | wb_to_front_bypass.ecall_en;
assign id_to_if_bypass.brjmp_pc =   exe_to_front_bypass.ecall_en ? `NULL    : 
                                    wb_to_front_bypass.ecall_en  ? ecall_pc : brjmp_pc;



endmodule
